package com.wen.controller.system;

import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.wen.common.enums.CodeEnum;
import com.wen.common.pojo.system.User;
import com.wen.common.utils.ResponseResult;
import com.wen.system.mapper.UserMapper;
import com.wen.system.oauth.OAuthProperties;
import com.wen.system.oauth.OAuthSimpleFactory;
import com.wen.system.service.OAuthService;
import com.xkcoding.http.HttpUtil;
import com.xkcoding.http.support.hutool.HutoolImpl;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

/**
 * 第三方登录认证
 * <p>
 * OAuth2 认证流程：<p>
 * OAuth服务端后端有个authorize接口，提供第三方的客户端认证（给第三方授权），<p>
 * 前端通过OAuth服务前端跳转到authorize接口，并带上client_id,redirect_uri等参数，<p>
 * 用户同意授权后，OAuth服务端会重定向到redirect_uri，并带上code参数<p>
 * 前端拿到code后，通过code向OAuth服务端请求access_token，并带上client_secret等参数<p>
 * OAuth服务端返回access_token，前端通过access_token向资源服务器请求资源
 * <p>
 *
 * @author devin <devinlive@163.com>
 * @version 1.0
 * @since 2023/08/24 14:53
 */
@Slf4j
@RestController
@RequestMapping("/system/oauth")
//@Api(tags = "第三方登录认证")
public class OAuthController {

    @Autowired
    private OAuthSimpleFactory factory;
    @Autowired
    private OAuthProperties oAuthProperties;
    @Autowired
    private OAuthService oAuthService;

    public static void main(String[] args) {

    }

    public void setAlipayClient() throws AlipayApiException {

        String privateKey = "<-- 请填写您的应用私钥，例如：MIIEvQIBADANB ... ... -->";
        String alipayPublicKey = "<-- 请填写您的支付宝公钥，例如：MIIBIjANBg... -->";
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
        alipayConfig.setAppId("<-- 请填写您的AppId，例如：2019091767145019 -->");
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF8");
        alipayConfig.setSignType("RSA2");
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setCode("4b203fe6c11548bcabd8da5bb087a83b");
        request.setGrantType("authorization_code");
        request.setRefreshToken("201208134b203fe6c11548bcabd8da5bb087a83b");
        AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
        System.out.println(response.getBody());
        if (response.isSuccess()) {
            System.out.println("调用成功");

        } else {
            System.out.println("调用失败");
            // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
            // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
            // System.out.println(diagnosisUrl);
        }
        AlipayUserInfoShareRequest request1 = new AlipayUserInfoShareRequest();
        AlipayUserInfoShareResponse response1 = alipayClient.execute(request1,"access_token");
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
    }

    @RequestMapping("/url")
    public void url(HttpServletResponse response,String type) throws IOException {
        AuthRequest authRequest = factory.getAuthRequest(type);
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

    @RequestMapping("/render")
    public void renderAuth(HttpServletResponse response) throws IOException {
        AuthRequest authRequest = getAuthRequest();
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

    @RequestMapping("/callback")
    public Object login(AuthCallback callback) {
        AuthRequest authRequest = getAuthRequest();
        HttpUtil.setHttp(new HutoolImpl());
        return authRequest.login(callback);
    }

    private AuthRequest getAuthRequest() {
        return factory.getAuthRequest("Gitee");
    }

    // 刷新Access Token地址：https://gitee.com/oauth/token?grant_type=refresh_token&refresh_token={refresh_token}
    @GetMapping("/getOAuthUrl")
    public ResponseResult<?> getOAuthGieeUrl() {
        OAuthProperties.GiteeProperties giteeProperties = oAuthProperties.getGitee();
        // 授权地址 ,进行Encode转码
        String authorizeURL = giteeProperties.getAuthorizeURI();

        // 回调地址 ,回调地址要进行Encode转码
        String redirectUri = giteeProperties.getRedirectURI();

        // 用于第三方应用防止CSRF攻击
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");

        // 拼接url
        String url = authorizeURL +
                "?client_id=" + giteeProperties.getClientId() +
                "&response_type=code" +
                // 转码
                "&redirect_uri=" + giteeProperties.getRedirectURI() +
                "&state=" + uuid +
                "&scope=user_info";

        return ResponseResult.success(url);
    }

    @GetMapping("/gitee/callback")
    public ResponseResult<?> giteeCallback(String code, String state, HttpServletRequest request, HttpServletResponse response) {
        // todo此处需要通知前端认证结果
        log.info("code : {}", code);
        String code1 = request.getParameter("code");
        log.info("code -> request : {}", code1);

        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            params.put(name, request.getParameter(name));
            System.out.println(name + " = " + request.getParameter(name));
        }
        if (code != null && code.length() != 0) {
            // 获取access_token
            ResponseResult accessTokenResp = getAccessToken(code);

            JSONObject accessTokenData = (JSONObject) accessTokenResp.getData();
            AuthToken authToken = JSONUtil.toBean(accessTokenData, AuthToken.class);
            String accessToken = accessTokenData.getStr("access_token");
            ResponseResult<?> userInfo = getUserInfo(accessToken);
            if (userInfo.getCode().equals("200")) {
                JSONObject userInfoData = (JSONObject) userInfo.getData();
                // 注册用户
                oAuthService.addOAuthUser(userInfoData);
                userInfoData.set("auth_token_info", authToken);
                return ResponseResult.success(userInfoData);
            }
        }

//        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
//        map.add("error", params.get("error"));
//        map.add("error_description",  params.get("error_description"));
//        map.add("state",params.get("state"));
        return ResponseResult.error(CodeEnum.CLIENT_AUTH_ERROR);
    }
    @Autowired
    UserMapper userMapper;
    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("tran")
    public void t(){
        User user = new User();
        user.setUserName("tran");
        user.setNickName("tran");
        user.setPassword("123456");
        user.setEmail("tran");
        userMapper.insert(user);
        int i = 1/0;
    }





    /**
     * 根据授权码获取 access_token
     *
     * @param code 授权码
     *             https://gitee.com/oauth/token
     *             ?grant_type=authorization_code
     *             &code={code}
     *             &client_id={client_id}
     *             &redirect_uri={redirect_uri}
     *             &client_secret={client_secret}
     */
    public ResponseResult<?> getAccessToken(String code) {
        OAuthProperties.GiteeProperties giteeProperties = oAuthProperties.getGitee();
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", "authorization_code");
        map.add("code", code);
        map.add("client_id", giteeProperties.getClientId());
        map.add("client_secret", giteeProperties.getClientSecret());
        map.add("redirect_uri", giteeProperties.getRedirectURI());

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity(giteeProperties.getTokenURI(), request, String.class);
        log.info("response : {}", response);

        if (response.getStatusCode() == HttpStatus.OK) {
            // 解析响应获取access_token
            JSONObject jsonObject = new JSONObject(response.getBody());
            String accessToken = jsonObject.getStr("access_token");
            return ResponseResult.success(jsonObject);
        }
        return null;
    }

    @GetMapping("/getUserInfo")
    public ResponseResult<?> getUserInfo(String accessToken) {
        OAuthProperties.GiteeProperties giteeProperties = oAuthProperties.getGitee();
        log.info("token : {}", accessToken);
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("Authorization", "Bearer " + accessToken);


        HttpEntity<String> request = new HttpEntity<>(headers);
        ResponseEntity<String> response = restTemplate.exchange(giteeProperties.getUserInfoURI(), HttpMethod.GET, request, String.class);
        log.info("response : {}", response);

        if (response.getStatusCode() == HttpStatus.OK) {
//            JSONObject jsonObject = JSONUtil.parseObj(response.getBody(), true); // 解决null值的序列化问题
            JSONObject jsonObject = new JSONObject(response.getBody());
            return ResponseResult.success(jsonObject);
        }
        return ResponseResult.error("", "获取用户信息失败");
    }

    //    public void getRepos(String accessToken) {
//        RestTemplate restTemplate = new RestTemplate();
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//        headers.add("Authorization", "token " + accessToken);
//
//        HttpEntity<String> request = new HttpEntity<>(headers);
//        ResponseEntity<String> response = restTemplate.exchange(giteeProperties.getRepos(), HttpMethod.GET, request, String
//                .class);
//        log.info("response : {}", response);
//    }
//    public void getRepo(String accessToken, String repoId) {
//        RestTemplate restTemplate = new RestTemplate();
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//        headers.add("Authorization", "token " + accessToken);
//
//        HttpEntity<String> request = new HttpEntity<>(headers);
//        ResponseEntity<String> response = restTemplate.exchange(giteeProperties.getRepo(repoId), HttpMethod.GET
//                , request, String.class);
//        log.info("response : {}", response);
//    }
    @GetMapping("/redirectUrl")
    public ResponseEntity<Void> redirectWithQueryParamsIfy(String uri, @Nullable MultiValueMap<String, String> queryParams) {

        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uri)
                .queryParams(queryParams);
        URI redirectUri = builder.build().toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(redirectUri);

        log.info("Redirect uri : {}", redirectUri);

        ResponseEntity<Void> responseEntity = new ResponseEntity<>(headers, HttpStatus.FOUND);
        return responseEntity;
    }

    @GetMapping("/test")
    public ResponseEntity<String> test() {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<String> request = new HttpEntity<>(headers);
        return restTemplate.postForEntity("https://www.baidu.com", request, String.class);
    }




    private final Map<String, String> scanStatusMap = new HashMap<>();

    /*
        前提条件！用户只有登录后才能扫码
        首先认证中心要知道用户有没有登陆，如果没有登录重定向一个登录界面登录。用户拿
        用户扫码后去请求二维码的url，带上二维码唯一id
        服务端更新二维码状态为`已扫描，待确认`，并返回一个临时token
        用户点击确认登录，请求服务器带上临时token，和用户信息
        服务器验证临时token，并更新二维码状态为`已确认`，并返回正式token
        前端保存token，并登录成功
    */
    // 是由sso传回的结果
    @GetMapping("/scan")
    public ResponseEntity<Void> scanQRCode(@RequestParam("uuid") String uuid) {
        // 判断是不是客户端/有没有登录
        // 判断二维码有效性
        // 更新二维码状态，下发ticket
        scanStatusMap.put(uuid, "已扫描，待确认");
        UUID ticket = UUID.randomUUID();

        // 设置查询参数
        MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
        queryParams.add("uuid", uuid);
        queryParams.add("ticket", ticket.toString());

        String btn = " <button type=\"button\">确认登录</button>";
        return this.redirectWithQueryParamsIfy("http://192.168.50.188:8888/sso", queryParams);
    }

    @PostMapping("/confirm")
    public ResponseResult<?> confirmQRCode(@RequestParam("uuid") String uuid,
                                           @RequestBody Map<String, String> body) {
        // 判断是不是客户端/有没有登录
        // 判断二维码有效性
        if (!scanStatusMap.containsKey(uuid)
                || !scanStatusMap.get(uuid).equals("已扫描，待确认")
                || !body.containsKey("ticket")
                || !body.containsKey("accessToken")) {
            return ResponseResult.error("7001", "二维码失效，请重新扫描");
        }
        // 更新二维码状态
        scanStatusMap.put(uuid, "已确认");
        log.info("ticket : {}", body.get("ticket"));
        log.info("access token : {}", body.get("accessToken"));
        return ResponseResult.success();
    }

    /**
     * 通过回调的方式使前端一直执行callback，可以前端主动一直发请求
     * @param uuid
     * @param callback
     * @return
     */
    // 检查二维码扫描状态
    @PostMapping("/getQrStat")
    public ResponseResult<?> getQRCodeState(@RequestBody String uuid, @RequestParam("callback") String callback) {
        // todo需要移除已扫描的,todo二维码的过期时间
        String stat = scanStatusMap.get(uuid);

        // callback: check({"barcodeStatus":"waiting","stat":"ok"})
            JSONObject qrRes = new JSONObject();
        if (callback != null && callback.length() > 0) {
            qrRes.set("barcodeStatus", "undefined");
            qrRes.set("stat", stat);
            StringBuilder cbName = new StringBuilder(callback);
            qrRes.set("callback", cbName.append("(").append(qrRes).append(")"));
        }

        log.info("QrCode state : {}",stat);
        return ResponseResult.success(qrRes);
    }

}
